// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_
#define IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_

#include <mach/mach.h>
#include <stdint.h>

#include <map>

#include "base/gtest_prod_util.h"
#include "base/mac/scoped_mach_port.h"
#include "base/macros.h"
#include "base/memory/scoped_vector.h"
#include "base/process/port_provider_mac.h"
#include "base/synchronization/lock.h"
#include "ipc/attachment_broker_privileged.h"
#include "ipc/ipc_export.h"
#include "ipc/mach_port_attachment_mac.h"

namespace base {
class PortProvider;
} // namespace base

namespace IPC {

// This class is a concrete subclass of AttachmentBrokerPrivileged for the
// OSX platform.
//
// An example of the typical process by which a Mach port gets brokered.
// Definitions:
//   1. Let there be three processes P1, U2, U3. P1 is privileged.
//   2. U2 wants to send a Mach port M2 to U3. If this port is inserted into P1,
//   it will be called M1. If it is inserted into U3, it will be called M3.
//   3. name() returns a serializable representation of a Mach port that can be
//   passed over chrome IPC.
//   4. pid() returns the process id of a process.
//
// Process:
//   1. U2 sends a AttachmentBrokerMsg_DuplicateMachPort message to P1. The
//   message contains name(M2), and pid(U3).
//   2. P1 extracts M2 into its own namespace, making M1.
//   3. P1 makes a new Mach port R in U3.
//   4. P1 sends a mach_msg with M1 to R.
//   5. P1 sends name(R) to U3.
//   6. U3 retrieves the queued message from R. The kernel automatically
//   translates M1 into the namespace of U3, making M3.
//
// The logic of this class is a little bit more complex becauese any or all of
// P1, U2 and U3 may be the same, and depending on the exact configuration,
// the creation of R may not be necessary.
//
// For the rest of this file, and the corresponding implementation file, R will
// be called the "intermediate Mach port" and M3 the "final Mach port".
class IPC_EXPORT AttachmentBrokerPrivilegedMac
    : public AttachmentBrokerPrivileged,
      public base::PortProvider::Observer {
public:
    explicit AttachmentBrokerPrivilegedMac(base::PortProvider* port_provider);
    ~AttachmentBrokerPrivilegedMac() override;

    // IPC::AttachmentBroker overrides.
    bool SendAttachmentToProcess(
        const scoped_refptr<IPC::BrokerableAttachment>& attachment,
        base::ProcessId destination_process) override;
    void DeregisterCommunicationChannel(Endpoint* endpoint) override;

    // IPC::Listener overrides.
    bool OnMessageReceived(const Message& message) override;

    // base::PortProvider::Observer override.
    void OnReceivedTaskPort(base::ProcessHandle process) override;

private:
    FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
        InsertRight);
    FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
        InsertSameRightTwice);
    FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
        InsertTwoRights);
    using MachPortWireFormat = internal::MachPortAttachmentMac::WireFormat;

    // Contains all the information necessary to broker an attachment into a
    // destination process. The only thing that prevents an AttachmentPrecusor
    // from being immediately processed is if |port_provider_| does not yet have a
    // task port for |pid|.
    class IPC_EXPORT AttachmentPrecursor {
    public:
        AttachmentPrecursor(const base::ProcessId& pid,
            base::mac::ScopedMachSendRight port_to_insert,
            const BrokerableAttachment::AttachmentId& id);
        ~AttachmentPrecursor();

        // Caller takes ownership of |port_|.
        base::mac::ScopedMachSendRight TakePort();

        base::ProcessId pid() const { return pid_; }
        const BrokerableAttachment::AttachmentId id() const { return id_; }

    private:
        // The pid of the destination process.
        const base::ProcessId pid_;
        // The final Mach port, as per definition at the top of this file.
        base::mac::ScopedMachSendRight port_;
        // The id of the attachment.
        const BrokerableAttachment::AttachmentId id_;
        DISALLOW_COPY_AND_ASSIGN(AttachmentPrecursor);
    };

    // Contains all the information necessary to extract a send right and create
    // an AttachmentPrecursor. The only thing that prevents an AttachmentExtractor
    // from being immediately processed is if |port_provider_| does not yet have a
    // task port for |source_pid|.
    class IPC_EXPORT AttachmentExtractor {
    public:
        AttachmentExtractor(const base::ProcessId& source_pid,
            const base::ProcessId& dest_pid,
            mach_port_name_t port,
            const BrokerableAttachment::AttachmentId& id);
        ~AttachmentExtractor();

        base::ProcessId source_pid() const { return source_pid_; }
        base::ProcessId dest_pid() const { return dest_pid_; }
        mach_port_name_t port() const { return port_to_extract_; }
        const BrokerableAttachment::AttachmentId id() const { return id_; }

    private:
        const base::ProcessId source_pid_;
        const base::ProcessId dest_pid_;
        mach_port_name_t port_to_extract_;
        const BrokerableAttachment::AttachmentId id_;
    };

    // IPC message handlers.
    void OnDuplicateMachPort(const Message& message);

    // Duplicates the Mach port referenced from |wire_format| from
    // |source_process| into |wire_format|'s destination process.
    MachPortWireFormat DuplicateMachPort(const MachPortWireFormat& wire_format,
        base::ProcessId source_process);

    // |task_port| is the task port of another process.
    // |port_to_insert| must be a send right in the current task's name space.
    // Creates an intermediate Mach port in |pid| and sends |port_to_insert| as a
    // mach_msg to the intermediate Mach port.
    // Returns the intermediate port on success, and MACH_PORT_NULL on failure.
    // This method takes ownership of |port_to_insert|. On success, ownership is
    // passed to the intermediate Mach port.
    mach_port_name_t CreateIntermediateMachPort(
        mach_port_t task_port,
        base::mac::ScopedMachSendRight port_to_insert);

    // Extracts a copy of the send right to |named_right| from |task_port|.
    // Returns MACH_PORT_NULL on error.
    base::mac::ScopedMachSendRight ExtractNamedRight(
        mach_port_t task_port,
        mach_port_name_t named_right);

    // Copies an existing |wire_format|, but substitutes in a different mach port.
    MachPortWireFormat CopyWireFormat(const MachPortWireFormat& wire_format,
        uint32_t mach_port);

    // |wire_format.destination_process| must be this process.
    // |wire_format.mach_port| must be the final Mach port.
    // Consumes a reference to |wire_format.mach_port|, as ownership is implicitly
    // passed to the consumer of the Chrome IPC message.
    // Makes an attachment, queues it, and notifies the observers.
    void RoutePrecursorToSelf(AttachmentPrecursor* precursor);

    // |wire_format.destination_process| must be another process.
    // |wire_format.mach_port| must be the intermediate Mach port.
    // Ownership of |wire_format.mach_port| is implicitly passed to the process
    // that receives the Chrome IPC message.
    // Returns |false| on irrecoverable error.
    bool RouteWireFormatToAnother(const MachPortWireFormat& wire_format);

    // Atempts to broker all precursors whose destination is |pid|. Has no effect
    // if |port_provider_| does not have the task port for |pid|.
    void SendPrecursorsForProcess(base::ProcessId pid);

    // Brokers a single precursor into the task represented by |task_port|.
    // Returns |false| on irrecoverable error.
    bool SendPrecursor(AttachmentPrecursor* precursor, mach_port_t task_port);

    // Add a precursor to |precursors_|. Takes ownership of |port|.
    void AddPrecursor(base::ProcessId pid,
        base::mac::ScopedMachSendRight port,
        const BrokerableAttachment::AttachmentId& id);

    // Atempts to process all extractors whose source is |pid|. Has no effect
    // if |port_provider_| does not have the task port for |pid|.
    void ProcessExtractorsForProcess(base::ProcessId pid);

    // Processes a single extractor whose source pid is represented by
    // |task_port|.
    void ProcessExtractor(AttachmentExtractor* extractor, mach_port_t task_port);

    // Add an extractor to |extractors_|.
    void AddExtractor(base::ProcessId source_pid,
        base::ProcessId dest_pid,
        mach_port_name_t port,
        const BrokerableAttachment::AttachmentId& id);

    // The port provider must live at least as long as the AttachmentBroker.
    base::PortProvider* port_provider_;

    // For each ProcessId, a vector of precursors that are waiting to be
    // sent.
    std::map<base::ProcessId, ScopedVector<AttachmentPrecursor>*> precursors_;
    base::Lock precursors_lock_;

    // For each ProcessId, a vector of extractors that are waiting to be
    // processed.
    std::map<base::ProcessId, ScopedVector<AttachmentExtractor>*> extractors_;
    base::Lock extractors_lock_;

    DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMac);
};

} // namespace IPC

#endif // IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_
